/***** BEGIN LICENSE BLOCK *****
 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Common Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.eclipse.org/legal/cpl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
 * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
 * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the CPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the CPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/
package asruby.runtime {

import flash.utils.Dictionary;


/**
 * FIXME: This version is faster than the previous, but both suffer from a
 * crucial flaw: It is impossible to create an ObjectSpace with an iterator
 * that doesn't either: a. hold on to objects that might otherwise be collected
 * or b. have no way to guarantee that a call to hasNext() will be correct or
 * that a subsequent call to next() will produce an object. For our purposes,
 * for now, this may be acceptable.
 */
public class ObjectSpace {
    private  var deadReferences: Array =  new Array();
    private  var top:Object;

    private  var deadIdentityReferences: Array =  new Array();
    private  var identities: Dictionary =  new Dictionary();
    private  var identitiesByObject: Dictionary =  new Dictionary();

    private  var maxId: Number =  4; // Highest reserved id

    public  function idOf(rubyObject:IRubyObject):Number{
       
        var longId: Number =   identitiesByObject[rubyObject];
        if (longId == undefined) {
            longId = createId(rubyObject);
        }
        return longId;
    
    }

    private  function createId(object:IRubyObject): Number{
        //cleanIdentities();
        maxId += 2; // id must always be even   
        identities[maxId] = new Object(object, maxId, deadIdentityReferences);
        identitiesByObject.put(object, maxId);
        return maxId;
    }
/*
    public  function id2ref(id:Number):IRubyObject{
       
        cleanIdentities();
        var reference: IdReference = IdReference(identities).getValue(id);
        if (reference == null)
            return null;
        return IRubyObject(reference.getValue());
        
    }

    private  function cleanIdentities():void{
        var ref:IdReference;
        while ((ref = IdReference(deadIdentityReferences.poll()) != null))
            identities.remove(id);
    }
    
    public  function addFinalizer(object:IRubyObject, proc:IRubyObject):void{
        object.addFinalizer(proc);
    }
    
    public  function removeFinalizers(id:Number):void{
        var object: IRubyObject =  id2ref(id);
        if (object != null) {
            object.removeFinalizers();
        }
    }
*/    
    public  function add(object:IRubyObject):void{
        cleanup();
        top = new Object(object, deadReferences, top);
    }
/*
    public  function iterator(rubyClass:RubyModule):Iterator{
        var objList: Array =  new ArrayList();
        var current: WeakReferenceListNode =  top;
        while (current != null) {
            var obj: IRubyObject = (current).get();
            if (obj != null && rubyClass.isInstance(obj)) {
                objList.add(current);
            }

            current = current.nextNode;
        }
        return new Iterator() {
             var iter: Iterator =  objList.iterator();

            public  function hasNext():Boolean{
                throw new UnsupportedOperationException();
            }

            public  function next():Object{
                var obj: Object =  null;
                while (iter.hasNext()) {
                    var node: WeakReferenceListNode =  (WeakReferenceListNode)iter.next();

                    obj = node.get();

                    if (obj != null) break;
                }
                return obj;
            }

            public  function remove():void{
                throw new UnsupportedOperationException();
            }
        }; */

    private  function cleanup():void{
        var reference:Object;
        while ((reference = deadReferences.poll()) != null) {
            reference.remove();
        }
    }

/*     private class WeakReferenceListNode extends WeakReference {
        private  var prevNode:WeakReferenceListNode;
        private  var nextNode:WeakReferenceListNode;

        public  function WeakReferenceListNode(ref:Object, queue:ReferenceQueue, next:WeakReferenceListNode){
            super(ref, queue);

            this.nextNode = next;
            if (next != null) {
                next.prevNode = this;
            }
        }

        public  function remove():void{
            synchronized (ObjectSpace.this) {
                if (prevNode != null) {
                    prevNode.nextNode = nextNode;
                } else {
                    top = nextNode;
                }
                if (nextNode != null) {
                    nextNode.prevNode = prevNode;
                }
            } 
        }
    }

    private static class IdReference extends WeakReference {
        private  var id:Number;

        public  function IdReference(object:IRubyObject, id:Number, queue:ReferenceQueue){
            super(object, queue);
            this.id = id;
        }

        public  function id():Number{
            return id;
        }
    } */
}
}